/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.sentry.transport.apache;

import io.sentry.ITransportFactory;
import io.sentry.RequestDetails;
import io.sentry.SentryOptions;
import io.sentry.transport.ITransport;
import io.sentry.transport.RateLimiter;
import io.sentry.util.Objects;

import java.util.concurrent.TimeUnit;

import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
import org.apache.hc.core5.util.TimeValue;
import org.jetbrains.annotations.NotNull;

/**
 * Creates {@link ApacheHttpClientTransport}.
 *
 * @since 2021-06-15
 */
public final class ApacheHttpClientTransportFactory implements ITransportFactory {
    private final @NotNull TimeValue connectionTimeToLive;

    public ApacheHttpClientTransportFactory() {
        this(TimeValue.ofMinutes(1));
    }

    public ApacheHttpClientTransportFactory(final @NotNull TimeValue connectionTimeToLive) {
        this.connectionTimeToLive =
            Objects.requireNonNull(connectionTimeToLive, "connectionTimeToLive is required");
    }

    @Override
    public @NotNull ITransport create(
        final @NotNull SentryOptions options, final @NotNull RequestDetails requestDetails) {
        Objects.requireNonNull(options, "options is required");
        Objects.requireNonNull(requestDetails, "requestDetails is required");

        final PoolingAsyncClientConnectionManager connectionManager =
            PoolingAsyncClientConnectionManagerBuilder.create()
                .setPoolConcurrencyPolicy(PoolConcurrencyPolicy.LAX)
                .setConnectionTimeToLive(connectionTimeToLive)
                .setMaxConnTotal(options.getMaxQueueSize())
                .setMaxConnPerRoute(options.getMaxQueueSize())
                .build();

        final CloseableHttpAsyncClient httpclient =
            HttpAsyncClients.custom()
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                .setConnectionManager(connectionManager)
                .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                .setDefaultRequestConfig(
                    RequestConfig.custom()
                        // the time to establish the connection with the remote host
                        .setConnectTimeout(options.getConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
                        // the time to wait for a connection from the connection manager/pool
                        .setConnectionRequestTimeout(
                            options.getConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
                        // the time waiting for data, after establishing the connection; maximum time
                        // of inactivity between two data packets
                        .setResponseTimeout(options.getReadTimeoutMillis(), TimeUnit.MILLISECONDS)
                        .build())
                .build();
        final RateLimiter rateLimiter = new RateLimiter(options.getLogger());

        return new ApacheHttpClientTransport(options, requestDetails, httpclient, rateLimiter);
    }
}
